#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <psn_conf.h>

/*该程序需要依赖rc.local的代码*/

/*使用rc.local定义的EEPROM路径*/
#define USE_RCLOCAL_EEPROM 1

#if (USE_RCLOCAL_EEPROM)
	/*使用rc.local中的EEPROM配置文件获取EEPROM路径*/
	#include <device_i2ceeprom.h>
	#ifndef I2C_SYSROM_PATH
		#error "在rc.local/device_i2ceeprom.h中没有I2C_SYSROM_PATH的定义 [ERROR]"
	#endif
#else
	/*使用自定义的EEPROM路径*/
	#define I2C_SYSROM_PATH "/sys/bus/i2c/devices/0-0050/.sysrom"
#endif //end of: #if (USE_RCLOCAL_EEPROM)

#define PROG_NAME    "bcsysmgr"
#define PROG_VERSION "v1.0"

#define dtags PROG_NAME

#define debug 0
#if (debug)
	#define dmsg(fmt, args...) printf( dtags ",%d:%s(...): " fmt, __LINE__, __func__, ##args)
#else
	#define dmsg(fmt, args...)
#endif

#define BUF_SIZE_1024  1024
#define BUF_SIZE_256   256

#define BINCMD_CLR_REG  0
#define BINCMD_SET_PSN  1
#define BINCMD_GET_PSN  2

#define MGRCMD_CLR_REG  0
#define MGRCMD_SET_PSN  1
#define MGRCMD_GET_PSN  2
#define MGRCMD_SET_MAC0 3
#define MGRCMD_SET_MAC1 4
#define MGRCMD_GET_MAC0 5
#define MGRCMD_GET_MAC1 6
#define MGRCMD_GET_HELP 100

#define CMD_HEAD_HEX  0xE9
#define CMD_TAIL_HEX  0x9E

#define CMD_IDX_HEAD 0
#define CMD_IDX_ID   1
#define CMD_IDX_LEN  2
#define CMD_IDX_DATA 3

#define CMD_LEN_HEAD 3
#define CMD_LEN_MIN  5
#define CMD_LEN_SUM  1
#define CMD_LEN_TAIL 1

/*psn offset and char total*/
#define DATA_PSN_OFFSET 0
#define BINDAT_PSN_LENGTH 16

#define PINFO_ALIAS_LEN 8
#define PINFO_ALIAS_IDX 0
#define PINFO_YEAR_LEN 2
#define PINFO_YEAR_IDX ((PINFO_ALIAS_IDX)+(PINFO_ALIAS_LEN))
#define PINFO_MONTH_LEN 2
#define PINFO_MONTH_IDX ((PINFO_YEAR_IDX)+(PINFO_YEAR_LEN))
#define PINFO_DBSNH_LEN 2
#define PINFO_DBSNH_IDX ((PINFO_MONTH_IDX)+(PINFO_MONTH_LEN))
#define PINFO_DBSNL_LEN 2
#define PINFO_DBSNL_IDX ((PINFO_DBSNH_IDX)+(PINFO_DBSNH_LEN))

#define MAC_STR_LENGTH  17
#define MAC_DEF_PMACHD  "BC"
#define MAC_ERR_SENDCMD 0XFD
#define MAC_ERR_READCMD 0XFE
#define MAC_ERR_PSNTYPE 0XFF

typedef struct mgrcmd_t
{
	int   id;
	char  *name;
	char  *help;
	int   argc;
}sysbin_cmd;

sysbin_cmd mgrcmd_list[] = {
	{
		.id   = MGRCMD_SET_PSN,
		.name = "-setpsn",
		.help = "set product serial number",
		.argc = 3,
	},
	{
		.id   = MGRCMD_GET_PSN,
		.name = "-getpsn",
		.help = "get product serial number",
		.argc = 2,
	},
	{
		.id   = MGRCMD_SET_MAC0,
		.name = "-seteth0mac",
		.help = "auto set eth0 mac address refer to psn",
		.argc = 2,
	},
	{
		.id   = MGRCMD_SET_MAC1,
		.name = "-seteth1mac",
		.help = "auto set eth1 mac address refer to psn",
		.argc = 2,
	},
	{
		.id   = MGRCMD_GET_MAC0,
		.name = "-geteth0mac",
		.help = "get eth0 mac address refer to psn",
		.argc = 2,
	},
	{
		.id   = MGRCMD_GET_MAC1,
		.name = "-geteth1mac",
		.help = "get eth1 mac address refer to psn",
		.argc = 2,
	},
	{
		.id   = MGRCMD_GET_HELP,
		.name = "-ineedhelp",
		.help = "show this help message",
		.argc = 2,
	},
};


void fill_mgrcmd_common(int cmdid, int cmdlen, char *cmdbuf)
{
	cmdbuf[CMD_IDX_HEAD]  = CMD_HEAD_HEX;
	cmdbuf[CMD_IDX_ID]    = cmdid;
	cmdbuf[CMD_IDX_LEN]   = cmdlen;
	cmdbuf[cmdlen-2]      = 0;
	cmdbuf[cmdlen-1]      = CMD_TAIL_HEX;
}

int create_mgrcmd_package(int cmdid, char *cmdbuf, char *databuf, int len)
{
	int cmdlen  = 0;
	int dataidx = 0;

	switch(cmdid)
	{
		case BINCMD_SET_PSN:
			{
				if(len != BINDAT_PSN_LENGTH)
				{
					fprintf(stderr, "error psn length: %d, need: %d\n", len, BINDAT_PSN_LENGTH);
					return -1;
				}
				cmdlen = CMD_LEN_HEAD+BINDAT_PSN_LENGTH+CMD_LEN_SUM+CMD_LEN_TAIL;

				fill_mgrcmd_common(cmdid, cmdlen, cmdbuf);
				
				for(dataidx=0; dataidx < BINDAT_PSN_LENGTH; dataidx++)
				{
					cmdbuf[CMD_IDX_DATA+dataidx] = databuf[dataidx];
				}
				return cmdlen;
				break;
			}
		case MGRCMD_GET_PSN:
			{
				cmdlen = CMD_LEN_HEAD+CMD_LEN_SUM+CMD_LEN_TAIL;
				fill_mgrcmd_common(cmdid, cmdlen, cmdbuf);
				return cmdlen;
				break;
			}
		case MGRCMD_CLR_REG:
			{
				cmdlen = CMD_LEN_HEAD+CMD_LEN_SUM+CMD_LEN_TAIL;
				fill_mgrcmd_common(cmdid, cmdlen, cmdbuf);
				return cmdlen;
				break;
			}
		default:
			{
				fprintf(stderr, "unkown cmd type!\n");
				return -1;
			}
	}
	return -1;
}

int write_sysbin(char *buf, int len)
{
	int   ret  = -1;

	int  sysbin_fd = -1;

    sysbin_fd = open(I2C_SYSROM_PATH, O_RDWR);
    if (sysbin_fd < 0) 
	{
		fprintf(stderr, "open sysbin error!\n");
		return -1;
	}
	dmsg("open sysbin ok!\n");

    ret = write(sysbin_fd, buf, len);
    if(ret != len) 
	{
		fprintf(stderr, "write sysbin error!\n");
		close(sysbin_fd);
		return -1;
	}
	close(sysbin_fd);
	dmsg("write sysbin ok!\n");
	return 0;
}

int read_sysbin(char *buf, int len)
{
	int   ret  = -1;
	int   rlen = 0;

	int  sysbin_fd = -1;

    sysbin_fd = open(I2C_SYSROM_PATH, O_RDWR);
    if (sysbin_fd < 0) 
	{
		fprintf(stderr, "open sysbin error!\n");
		return -1;
	}
	dmsg("open sysbin ok!\n");

    ret = read(sysbin_fd, buf, len);
    if(ret != len) 
	{
		fprintf(stderr, "read sysbin error!\n");
		close(sysbin_fd);
		return -1;
	}
	close(sysbin_fd);
	dmsg("read sysbin : %s\n", buf);
	return ret;
}

int send_sysbin_cmd(int cmdid, char *databuf, int len)
{
	int   cmdlen = 0;
	char  cmdbuf[BUF_SIZE_256] = {0};

	cmdlen = create_mgrcmd_package(cmdid, cmdbuf, databuf, len);

	if(cmdlen <= 0)
	{
		fprintf(stderr, "error create sysbin cmd\n");
		return -1;
	}
	dmsg("get cmdlen: %d\n", cmdlen);

	if(0 != write_sysbin(cmdbuf, cmdlen))
	{
		fprintf(stderr, "error write sysbin cmd\n");
		return 0;
	}
	dmsg("send ok\n");
	return 0;
}

int send_sysbin_clrcmd(void)
{
	if(0 != send_sysbin_cmd(BINCMD_CLR_REG, NULL, 0))
	{
		fprintf(stderr, "clear cmd failed\n");
		return -1;
	}
}

void show_help(void)
{
#if 0
	int  idx = 0; 
	int  max = 0;
	char buf[BUF_SIZE_256] = {0};

	fprintf(stdout, "\nUsage: %s CMD params\n", PROG_NAME);
	fprintf(stdout, "CMD List:\n");
	max = sizeof(mgrcmd_list)/sizeof(mgrcmd_list[0]);
	for(idx = 0; idx < max; idx++)
	{
		sprintf(buf, "%s, %s",mgrcmd_list[idx].name, mgrcmd_list[idx].help);
		fprintf(stdout, "%s\n", buf);
	}
	fprintf(stdout, "\n");
#endif
}

void show_hidehelp(void)
{
#if 1
	int  idx = 0; 
	int  max = 0;
	char buf[BUF_SIZE_256] = {0};

	fprintf(stdout, "\nUsage: %s CMD params\n", PROG_NAME);
	fprintf(stdout, "CMD List:\n");
	max = sizeof(mgrcmd_list)/sizeof(mgrcmd_list[0]);
	for(idx = 0; idx < max; idx++)
	{
		sprintf(buf, "%s, %s",mgrcmd_list[idx].name, mgrcmd_list[idx].help);
		fprintf(stdout, "%s\n", buf);
	}
	fprintf(stdout, "\n");
#endif
}

int parse_cmds(int argc, char **argv)
{
	int idx   =  0;
	int max   =  0;
	int cmdid = -1;

	if( 2 > argc)
	{
		return -1;
	}

	max = sizeof(mgrcmd_list)/sizeof(mgrcmd_list[0]);
	for(idx = 0; idx < max; idx++)
	{
		if(0 == strcmp(mgrcmd_list[idx].name, argv[1]))
		{
			cmdid = mgrcmd_list[idx].id;
			break;
		}
	}
	if((cmdid != -1) && (mgrcmd_list[idx].argc !=  argc))
	{
		fprintf(stderr, "error params, argc: %d, need: %d!\n", argc, mgrcmd_list[idx].argc);
		cmdid = -1;
	}
	return cmdid;
}



int find_psnconf_pidx(char *alias)
{
	int pidx = 0;
	int pmax = sizeof(psnconf_list)/sizeof(psnconf_list[0]);
	for(pidx = 0; pidx < pmax; pidx++)
	{
		if(0 == strcmp(alias, psnconf_list[pidx].alias))
		{
			break;
		}
	}
	if(pidx >= pmax)
	{
		return -1;
	}
	return pidx;
}

int set_mac_default(char* ethname, unsigned char errno)
{
	char  wbuf[BUF_SIZE_256] = {0};
	char  macstr[MAC_STR_LENGTH+1] = {0};
	sprintf(macstr, "%s:%02x:EE:EE:00:01", MAC_DEF_PMACHD, errno);
	sprintf(wbuf, "ifconfig %s hw ether %s", ethname, macstr);
	system(wbuf);
	fprintf(stdout, "set default %s mac : %s done!\n", ethname, macstr);
	return 0;
}

int set_mac_from_psn(char *ethname, int ethoffset)
{
	int   rlen = 0;
	int   pidx = -1;
	char  wbuf[BUF_SIZE_256] = {0};
	char  rbuf[BUF_SIZE_256] = {0};

	char  pyear[PINFO_YEAR_LEN+1]  = {0};
	char  pmonth[PINFO_MONTH_LEN+1]= {0};
	char  pdbsnH[PINFO_DBSNH_LEN+1]= {0};
	char  pdbsnL[PINFO_DBSNL_LEN+1]= {0};

	unsigned char pcode = 0;

	char  macstr[MAC_STR_LENGTH+1] = {0};

	char  tmp_alias[PINFO_ALIAS_LEN+1] = {0};

	if(0 != send_sysbin_cmd(MGRCMD_GET_PSN, NULL, 0))
	{
		fprintf(stderr, "request psn failed\n");
		return MAC_ERR_SENDCMD;
	}
	dmsg("request psn ok!\n");
	rlen = read_sysbin(rbuf, BINDAT_PSN_LENGTH);
	if(rlen != BINDAT_PSN_LENGTH)
	{
		dmsg("read psn failed\n");
		return MAC_ERR_READCMD;
	}
	send_sysbin_clrcmd();

	fprintf(stdout, "get psn : %s\n", rbuf);

	strncpy(tmp_alias, rbuf+PINFO_ALIAS_IDX, PINFO_ALIAS_LEN);
	pidx = find_psnconf_pidx(tmp_alias);
	if(pidx < 0)
	{
		fprintf(stderr, "no product %s found!\n", tmp_alias);
		return MAC_ERR_PSNTYPE;
	}
	strncpy(pyear,  rbuf+PINFO_YEAR_IDX,  PINFO_YEAR_LEN);
	strncpy(pmonth, rbuf+PINFO_MONTH_IDX, PINFO_MONTH_LEN);
	strncpy(pdbsnH, rbuf+PINFO_DBSNH_IDX, PINFO_DBSNH_LEN);
	strncpy(pdbsnL, rbuf+PINFO_DBSNL_IDX, PINFO_DBSNL_LEN);

	pcode = psnconf_list[pidx].pcode + ethoffset;

	sprintf(macstr, "%s:%02x:%s:%s:%s:%s",
			psnconf_list[pidx].pmachd,
			pcode,
			pyear,
			pmonth,
			pdbsnH,
			pdbsnL);
	sprintf(wbuf, "ifconfig %s hw ether %s", ethname, macstr);
	system(wbuf);
	fprintf(stdout, "set %s mac : %s done!\n", ethname, macstr);
	return 0;
}

int get_mac_from_psn(char *ethname, int ethoffset)
{
	int   rlen = 0;
	int   pidx = -1;
	char  rbuf[BUF_SIZE_256] = {0};

	char  pyear[PINFO_YEAR_LEN+1]  = {'E','E','\0'};
	char  pmonth[PINFO_MONTH_LEN+1]= {'E','E','\0'};
	char  pdbsnH[PINFO_DBSNH_LEN+1]= {'0','0','\0'};
	char  pdbsnL[PINFO_DBSNL_LEN+1]= {'0','1','\0'};

	unsigned char pcode = 0;

	int   ret = 0;

	char  macstr[MAC_STR_LENGTH+1] = {0};

	char  tmp_alias[PINFO_ALIAS_LEN+1] = {0};

	if(0 != send_sysbin_cmd(BINCMD_GET_PSN, NULL, 0))
	{
		fprintf(stderr, "request psn failed\n");
		return MAC_ERR_SENDCMD;
	}
	dmsg("request psn ok!\n");
	rlen = read_sysbin(rbuf, BINDAT_PSN_LENGTH);
	if(rlen != BINDAT_PSN_LENGTH)
	{
		dmsg("read psn failed\n");
		send_sysbin_clrcmd();
		return MAC_ERR_READCMD;
	}

	send_sysbin_clrcmd();
	fprintf(stdout, "get psn : %s\n", rbuf);
	strncpy(tmp_alias, rbuf+PINFO_ALIAS_IDX, PINFO_ALIAS_LEN);
	pidx = find_psnconf_pidx(tmp_alias);
	if(pidx < 0)
	{
		fprintf(stderr, "no product %s found!\n", tmp_alias);
		return MAC_ERR_PSNTYPE;
	}
	strncpy(pyear,  rbuf+PINFO_YEAR_IDX,  PINFO_YEAR_LEN);
	strncpy(pmonth, rbuf+PINFO_MONTH_IDX, PINFO_MONTH_LEN);
	strncpy(pdbsnH, rbuf+PINFO_DBSNH_IDX, PINFO_DBSNH_LEN);
	strncpy(pdbsnL, rbuf+PINFO_DBSNL_IDX, PINFO_DBSNL_LEN);

	pcode = psnconf_list[pidx].pcode + ethoffset;

	sprintf(macstr, "%s:%02x:%s:%s:%s:%s",
			psnconf_list[pidx].pmachd,
			pcode,
			pyear,
			pmonth,
			pdbsnH,
			pdbsnL);

	fprintf(stdout, "get %s mac : %s\n", ethname, macstr);
	return 0;
}

int main(int argc, char **argv)
{
	int ret   = -1;
	int cmdid = -1;

	char  rbuf[BUF_SIZE_256] = {0};
	int   rlen = 0;

	cmdid = parse_cmds (argc, argv);
	if (cmdid < 0)
	{
		show_help ();
		return -1;
	}
	switch(cmdid)
	{
		case MGRCMD_SET_PSN:
			{
				if(0 != send_sysbin_cmd(MGRCMD_SET_PSN, argv[2], strlen(argv[2])))
				{
					fprintf(stderr, "set psn failed!\n");
					return -1;
				}
				fprintf(stdout, "set psn ok!\n");
				break;
			}
		case MGRCMD_GET_PSN:
			{
				if(0 != send_sysbin_cmd(MGRCMD_GET_PSN, NULL, 0))
				{
					fprintf(stderr, "request psn failed\n");
					return -1;
				}
				dmsg("request psn ok!\n");
				rlen = read_sysbin(rbuf, BINDAT_PSN_LENGTH);
				if(rlen != BINDAT_PSN_LENGTH)
				{
					dmsg("read psn failed\n");
					return -1;
				}
				fprintf(stdout, "get psn : %s\n", rbuf);
				send_sysbin_clrcmd();
				break;
			}
		case MGRCMD_GET_MAC0:
			{
				get_mac_from_psn("eth0", 0);
				break;
			}
		case MGRCMD_GET_MAC1:
			{
				get_mac_from_psn("eth1", 1);
				break;
			}
		case MGRCMD_SET_MAC0:
			{
				ret = set_mac_from_psn("eth0", 0);
				if(0 != ret)
				{
					set_mac_default("eth0", ret);
				}
				break;
			}
		case MGRCMD_SET_MAC1:
			{
				ret = set_mac_from_psn("eth1", 1);
				if(0 != ret)
				{
					set_mac_default("eth1", (unsigned char)ret);
				}
				break;
			}
		case MGRCMD_GET_HELP:
			{
				show_hidehelp();
				ret = 0;
				break;
			}
	}
	return ret;
}
